/**
 * @file camera_utils.c
 * @brief 摄像头操作工具函数实现
 * @version 1.1
 * @date 2023-05-20
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <sys/time.h>
#include <sys/stat.h>  // 添加对 chmod 的支持
#include <time.h>
#include <inttypes.h>
#include <curl/curl.h>
#include <pthread.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <errno.h>
#include "camera_utils.h"
#include "system_utils.h"
#include "thread_utils.h"

// 常量定义，使用全大写命名规范
#define MAX_RETRY_COUNT 20
#define CONNECTION_TIMEOUT_SEC 5

/**
 * @brief 获取当前时间戳（毫秒）
 * 
 * @return uint64_t 当前时间戳（毫秒）
 */
uint64_t get_timestamp_ms(void) {
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return (uint64_t)tv.tv_sec * 1000 + tv.tv_usec / 1000;
}

/**
 * @brief 初始化摄像头
 * 
 * @param camera 摄像头结构体指针
 * @param rtsp_url RTSP流URL
 * @param width 图像宽度
 * @param height 图像高度
 * @return int 成功返回0，失败返回负数错误码
 */
int camera_init(Camera *camera, const char *rtsp_url, int width, int height) {
    if (!camera || !rtsp_url) {
        log_message(LOG_LEVEL_ERROR, "相机初始化失败：无效的参数");
        return -1;
    }

    // 初始化相机结构体
    memset(camera, 0, sizeof(Camera));
    strncpy(camera->rtsp_url, rtsp_url, sizeof(camera->rtsp_url) - 1);
    camera->width = width;
    camera->height = height;
    camera->is_connected = false;
    camera->video_stream_idx = -1;

    // 初始化互斥锁，使用统一的thread_utils提供的函数
    if (create_mutex(&camera->mutex) != 0) {
        log_message(LOG_LEVEL_ERROR, "相机初始化失败：无法创建互斥锁");
        return -1;
    }

    // 分配内存
    camera->format_ctx = avformat_alloc_context();
    if (!camera->format_ctx) {
        log_message(LOG_LEVEL_ERROR, "无法分配AVFormatContext");
        destroy_mutex(&camera->mutex);
        return -1;
    }

    // 设置连接超时
    camera->format_ctx->interrupt_callback.callback = NULL; // 可以实现一个中断回调
    camera->format_ctx->interrupt_callback.opaque = camera;

    // 设置打开选项
    AVDictionary *options = NULL;
    av_dict_set(&options, "rtsp_transport", "tcp", 0);
    av_dict_set(&options, "stimeout", "5000000", 0); // 5秒超时
    av_dict_set(&options, "max_delay", "500000", 0); // 500毫秒最大延迟

    // 打开RTSP流
    int ret = avformat_open_input(&camera->format_ctx, camera->rtsp_url, NULL, &options);
    av_dict_free(&options);
    if (ret < 0) {
        char err_buf[256];
        av_strerror(ret, err_buf, sizeof(err_buf));
        log_message(LOG_LEVEL_ERROR, "无法打开RTSP流 '%s': %s", camera->rtsp_url, err_buf);
        avformat_free_context(camera->format_ctx);
        camera->format_ctx = NULL;
        destroy_mutex(&camera->mutex);
        return -2;
    }

    // 查找流信息
    ret = avformat_find_stream_info(camera->format_ctx, NULL);
    if (ret < 0) {
        char err_buf[256];
        av_strerror(ret, err_buf, sizeof(err_buf));
        log_message(LOG_LEVEL_ERROR, "无法查找流信息: %s", err_buf);
        avformat_close_input(&camera->format_ctx);
        destroy_mutex(&camera->mutex);
        return -3;
    }

    // 查找视频流
    for (int i = 0; i < camera->format_ctx->nb_streams; i++) {
        if (camera->format_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            camera->video_stream_idx = i;
            camera->video_stream = camera->format_ctx->streams[i];
            break;
        }
    }

    if (camera->video_stream_idx == -1) {
        log_message(LOG_LEVEL_ERROR, "无法找到视频流");
        avformat_close_input(&camera->format_ctx);
        destroy_mutex(&camera->mutex);
        return -4;
    }

    // 获取解码器
    const AVCodec *codec = avcodec_find_decoder(camera->video_stream->codecpar->codec_id);
    if (!codec) {
        log_message(LOG_LEVEL_ERROR, "无法找到解码器");
        avformat_close_input(&camera->format_ctx);
        destroy_mutex(&camera->mutex);
        return -5;
    }

    // 分配解码器上下文
    camera->codec_ctx = avcodec_alloc_context3(codec);
    if (!camera->codec_ctx) {
        log_message(LOG_LEVEL_ERROR, "无法分配解码器上下文");
        avformat_close_input(&camera->format_ctx);
        destroy_mutex(&camera->mutex);
        return -6;
    }

    // 复制编解码器参数
    ret = avcodec_parameters_to_context(camera->codec_ctx, camera->video_stream->codecpar);
    if (ret < 0) {
        char err_buf[256];
        av_strerror(ret, err_buf, sizeof(err_buf));
        log_message(LOG_LEVEL_ERROR, "无法复制编解码器参数: %s", err_buf);
        avcodec_free_context(&camera->codec_ctx);
        avformat_close_input(&camera->format_ctx);
        destroy_mutex(&camera->mutex);
        return -7;
    }

    // 打开解码器
    ret = avcodec_open2(camera->codec_ctx, codec, NULL);
    if (ret < 0) {
        char err_buf[256];
        av_strerror(ret, err_buf, sizeof(err_buf));
        log_message(LOG_LEVEL_ERROR, "无法打开解码器: %s", err_buf);
        avcodec_free_context(&camera->codec_ctx);
        avformat_close_input(&camera->format_ctx);
        destroy_mutex(&camera->mutex);
        return -8;
    }

    // 分配帧和包
    camera->frame = av_frame_alloc();
    camera->packet = av_packet_alloc();
    if (!camera->frame || !camera->packet) {
        log_message(LOG_LEVEL_ERROR, "无法分配帧或包");
        if (camera->frame) av_frame_free(&camera->frame);
        if (camera->packet) av_packet_free(&camera->packet);
        avcodec_free_context(&camera->codec_ctx);
        avformat_close_input(&camera->format_ctx);
        destroy_mutex(&camera->mutex);
        return -9;
    }

    // 创建缩放上下文
    int src_width = camera->codec_ctx->width;
    int src_height = camera->codec_ctx->height;
    
    camera->sws_ctx = sws_getContext(
        src_width, 
        src_height,
        camera->codec_ctx->pix_fmt,
        width, 
        height,
        AV_PIX_FMT_BGR24,
        SWS_BILINEAR,
        NULL, 
        NULL, 
        NULL
    );
    
    if (!camera->sws_ctx) {
        log_message(LOG_LEVEL_ERROR, "无法创建缩放上下文");
        av_frame_free(&camera->frame);
        av_packet_free(&camera->packet);
        avcodec_free_context(&camera->codec_ctx);
        avformat_close_input(&camera->format_ctx);
        destroy_mutex(&camera->mutex);
        return -10;
    }

    // 分配帧缓冲区
    int buffer_size = width * height * 3; // BGR24格式，每像素3字节
    camera->frame_buffer = (char *)malloc(buffer_size);
    if (!camera->frame_buffer) {
        log_message(LOG_LEVEL_ERROR, "无法分配帧缓冲区");
        sws_freeContext(camera->sws_ctx);
        av_frame_free(&camera->frame);
        av_packet_free(&camera->packet);
        avcodec_free_context(&camera->codec_ctx);
        avformat_close_input(&camera->format_ctx);
        destroy_mutex(&camera->mutex);
        return -11;
    }

    // 设置相机为已连接状态
    camera->is_connected = true;
    camera->last_frame_time = get_timestamp_ms();
    
    log_message(LOG_LEVEL_INFO, "摄像头初始化成功，URL: %s, 分辨率: %dx%d", rtsp_url, width, height);
    return 0;
}

/**
 * @brief 检查摄像头连接状态
 * 
 * @param camera 摄像头结构体指针
 * @return bool 已连接返回true，否则返回false
 */
bool camera_check_connection(Camera *camera) {
    if (!camera) {
        return false;
    }

    lock_mutex(&camera->mutex);
    
    // 检查格式上下文和解码器上下文是否有效
    bool connected = camera->is_connected && camera->format_ctx && camera->codec_ctx;
    
    // 检查自上次成功获取帧以来的时间
    if (connected) {
        uint64_t current_time = get_timestamp_ms();
        uint64_t elapsed_time = current_time - camera->last_frame_time;
        // 如果超过一定时间未收到帧，认为连接断开
        if (elapsed_time > CONNECTION_TIMEOUT_SEC * 1000) {
            camera->is_connected = false;
            connected = false;
            log_message(LOG_LEVEL_ERROR, "摄像头连接超时: %llu ms", (unsigned long long)elapsed_time);
        }
    }
    
    unlock_mutex(&camera->mutex);
    return connected;
}

/**
 * @brief 关闭摄像头并释放资源
 * 
 * @param camera 摄像头结构体指针
 */
void camera_close(Camera *camera) {
    if (!camera) {
        return;
    }

    lock_mutex(&camera->mutex);
    
    // 释放帧缓冲区
    if (camera->frame_buffer) {
        free(camera->frame_buffer);
        camera->frame_buffer = NULL;
    }

    // 释放缩放上下文
    if (camera->sws_ctx) {
        sws_freeContext(camera->sws_ctx);
        camera->sws_ctx = NULL;
    }

    // 释放帧和包
    if (camera->frame) {
        av_frame_free(&camera->frame);
    }
    
    if (camera->packet) {
        av_packet_free(&camera->packet);
    }

    // 关闭解码器上下文
    if (camera->codec_ctx) {
        avcodec_free_context(&camera->codec_ctx);
    }

    // 关闭格式上下文
    if (camera->format_ctx) {
        avformat_close_input(&camera->format_ctx);
    }

    // 设置连接状态为断开
    camera->is_connected = false;
    camera->video_stream_idx = -1;
    camera->video_stream = NULL;
    
    unlock_mutex(&camera->mutex);
    destroy_mutex(&camera->mutex);
    
    log_message(LOG_LEVEL_INFO, "摄像头已关闭，释放资源完成");
}

/**
 * @brief 获取摄像头的当前帧
 * 
 * 从摄像头获取当前视频帧数据。如果连接断开，会尝试重连并记录错误。
 * 函数内部有重试机制，最多尝试MAX_RETRY_COUNT次。
 * 
 * @param camera 摄像头结构体指针
 * @param frame_data 输出参数，用于存储帧数据指针
 * @param width 输出参数，用于存储帧宽度
 * @param height 输出参数，用于存储帧高度
 * @return int 成功返回0，失败返回负值(-1:参数错误, -2:连接错误, -3:帧获取超时)
 */
int camera_get_frame(Camera *camera, uint8_t **frame_data, int *width, int *height) {
    if (!camera || !frame_data || !width || !height) {
        log_message(LOG_LEVEL_ERROR, "相机: 获取帧失败，参数无效");
        return -1;
    }
    
    // 使用thread_utils提供的锁管理函数
    lock_mutex(&camera->mutex);
    
    if (!camera_check_connection(camera)) {
        unlock_mutex(&camera->mutex);
        log_message(LOG_LEVEL_ERROR, "获取帧失败：摄像头未连接");
        return -2;
    }

    int retry_count = 0;
    int ret = 0;
    bool frame_decoded = false;

    // 尝试最多MAX_RETRY_COUNT次解码帧
    while (!frame_decoded && retry_count < MAX_RETRY_COUNT) {
        // 读取一个数据包
        ret = av_read_frame(camera->format_ctx, camera->packet);
        if (ret < 0) {
            char err_buf[256];
            av_strerror(ret, err_buf, sizeof(err_buf));
            log_message(LOG_LEVEL_WARNING, "读取帧失败: %s (尝试 %d/%d)", 
                       err_buf, retry_count + 1, MAX_RETRY_COUNT);
            retry_count++;
            
            // 释放资源并暂停一下
            av_packet_unref(camera->packet);
            thread_sleep(10); // 暂停10毫秒，使用统一的thread_utils函数
            continue;
        }

        // 只处理视频流
        if (camera->packet->stream_index != camera->video_stream_idx) {
            av_packet_unref(camera->packet);
            continue; // 继续找视频帧
        }

        // 发送包到解码器
        ret = avcodec_send_packet(camera->codec_ctx, camera->packet);
        if (ret < 0) {
            char err_buf[256];
            av_strerror(ret, err_buf, sizeof(err_buf));
            log_message(LOG_LEVEL_WARNING, "发送包到解码器失败: %s (尝试 %d/%d)", 
                       err_buf, retry_count + 1, MAX_RETRY_COUNT);
            retry_count++;
            av_packet_unref(camera->packet);
            continue;
        }

        // 从解码器获取帧
        ret = avcodec_receive_frame(camera->codec_ctx, camera->frame);
        if (ret < 0) {
            char err_buf[256];
            av_strerror(ret, err_buf, sizeof(err_buf));
            if (ret == AVERROR(EAGAIN)) {
                // 需要更多输入
                av_packet_unref(camera->packet);
                continue;
            } else {
                log_message(LOG_LEVEL_WARNING, "从解码器接收帧失败: %s (尝试 %d/%d)", 
                           err_buf, retry_count + 1, MAX_RETRY_COUNT);
                retry_count++;
                av_packet_unref(camera->packet);
                continue;
            }
        }

        // 成功解码一帧
        frame_decoded = true;
    }

    // 检查是否成功获取帧
    if (!frame_decoded) {
        unlock_mutex(&camera->mutex);
        log_message(LOG_LEVEL_ERROR, "获取帧失败：尝试超过最大次数");
        return -3;
    }

    // 使用缩放上下文转换帧格式
    uint8_t *data[4] = {(uint8_t*)camera->frame_buffer, NULL, NULL, NULL};
    int linesize[4] = {camera->width * 3, 0, 0, 0}; // BGR24格式，每像素3字节
    
    sws_scale(
        camera->sws_ctx,
        (const uint8_t * const *)camera->frame->data,
        camera->frame->linesize,
        0,
        camera->codec_ctx->height,
        data,
        linesize
    );

    // 设置输出参数
    *frame_data = (uint8_t*)camera->frame_buffer;
    *width = camera->width;
    *height = camera->height;

    // 更新最后帧时间戳
    camera->last_frame_time = get_timestamp_ms();

    // 释放包
    av_packet_unref(camera->packet);

    unlock_mutex(&camera->mutex);
    return 0;
}

/**
 * @brief 释放当前帧资源
 * 
 * 在处理完当前帧后调用，以释放相关资源。在当前实现中主要用于未来扩展，
 * 因为帧缓冲区是被重用的。
 * 
 * @param camera 摄像头结构体指针
 */
void camera_release_frame(Camera *camera) {
    if (!camera) {
        return;
    }
    
    // 使用thread_utils提供的锁管理函数
    lock_mutex(&camera->mutex);
    
    // 注意：在当前实现中，我们不需要特别释放帧，因为帧缓冲区是重用的
    // 但可以添加任何必要的清理工作
    if (camera) {
        // 在这里可以添加必要的清理逻辑
    }
    
    unlock_mutex(&camera->mutex);
}

// 声明外部函数，由 camera_time_sync.c 提供
extern size_t write_memory_callback(void *contents, size_t size, size_t nmemb, void *userp);
extern int sync_camera_time(const CameraAuthInfo *camera_auth);

/**
 * @brief 检查数据是否是有效的JPEG
 */
static int is_valid_jpeg(unsigned char *data, size_t size) {
    // 对于非常小的数据包，可能不是图像
    if (size < 100) return 0;
    
    // JPEG文件应以0xFF 0xD8开始
    if (data[0] == 0xFF && data[1] == 0xD8) {
        return 1;
    }
    
    return 0;
}

/**
 * @brief 直接获取摄像头单张图像并保存到文件
 */
int capture_single_camera_image(const char *ip, const char *username, const char *password, 
                             CameraType camera_type, const char *save_dir, const char *filename) {
    if (!ip || !username || !password || !save_dir || !filename) {
        log_message(LOG_LEVEL_ERROR, "获取摄像头图像: 参数无效");
        return -1;
    }
    
    // 确保目录存在，使用 system_utils.h 中声明的全局函数
    if (create_directory(save_dir) != 0) {
        log_message(LOG_LEVEL_ERROR, "获取摄像头图像: 创建目录失败: %s", save_dir);
        return -1;
    }
    
    // 构建完整文件路径
    char filepath[512];
    snprintf(filepath, sizeof(filepath), "%s/%s", save_dir, filename);
    
    // 构造URL
    char url[256];
    
    // 根据摄像头类型选择正确的API
    if (camera_type == CAMERA_TYPE_400W) {
        // 400W摄像头使用特定URL
        snprintf(url, sizeof(url), "http://%s/ISAPI/Streaming/channels/102/picture", ip);
    } else {
        // 800W摄像头使用标准URL
        snprintf(url, sizeof(url), "http://%s/ISAPI/Streaming/channels/1/picture", ip);
    }
    
    // 初始化CURL
    CURL *curl = curl_easy_init();
    if (!curl) {
        log_message(LOG_LEVEL_ERROR, "获取摄像头图像: 初始化CURL失败");
        return -1;
    }

    // 使用内存缓冲区接收数据
    struct {
        char *memory;
        size_t size;
    } chunk;
    
    chunk.memory = malloc(1);  // 为了realloc
    chunk.size = 0;    // 当前没有数据
    
    // 设置CURL选项
    curl_easy_setopt(curl, CURLOPT_URL, url);
    curl_easy_setopt(curl, CURLOPT_USERNAME, username);
    curl_easy_setopt(curl, CURLOPT_PASSWORD, password);
    curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_memory_callback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 5L);
    
    // 执行请求
    CURLcode res = curl_easy_perform(curl);
    
    // 检查请求是否成功
    if(res != CURLE_OK) {
        log_message(LOG_LEVEL_ERROR, "获取摄像头图像失败 %s: %s", url, curl_easy_strerror(res));
        free(chunk.memory);
        curl_easy_cleanup(curl);
        return -1;
    }
    
    // 验证获取到的数据是否是有效的JPEG
    if (chunk.size < 100 || !is_valid_jpeg((unsigned char*)chunk.memory, chunk.size)) {
        log_message(LOG_LEVEL_ERROR, "收到无效的JPEG数据: %s (大小: %zu 字节)", url, chunk.size);
        free(chunk.memory);
        curl_easy_cleanup(curl);
        return -1;
    }
    
    // 写入文件
    FILE *fp = fopen(filepath, "wb");
    if (!fp) {
        log_message(LOG_LEVEL_ERROR, "获取摄像头图像: 打开文件失败: %s", filepath);
        free(chunk.memory);
        curl_easy_cleanup(curl);
        return -1;
    }
    
    size_t written = fwrite(chunk.memory, 1, chunk.size, fp);
    fclose(fp);
    
    if (written != chunk.size) {
        log_message(LOG_LEVEL_ERROR, "写入文件失败: %s", filepath);
        free(chunk.memory);
        curl_easy_cleanup(curl);
        return -1;
    }
    
    // 设置文件权限为777
    #ifdef __linux__
        if (chmod(filepath, 0777) != 0) {
            log_message(LOG_LEVEL_WARNING, "无法修改文件权限: %s", filepath);
        }
    #endif
    
    free(chunk.memory);
    curl_easy_cleanup(curl);
    log_message(LOG_LEVEL_INFO, "成功获取摄像头图像: %s (大小: %zu 字节)", filepath, chunk.size);
    return 0;
} 